/* ~.~ *-c-*
 *
 * Copyright (c) 2019, Jacky Quene <qdjiangzhikun@163.com>
 * 2019年 01月 31日 星期四 14:33:41 CST
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "protol_machine.h"
#include "Typedef.h"

bool InitProtolMachine(ProtolMachine **pProtolMach)
{
	*pProtolMach = (ProtolMachine *)pvPortMalloc(sizeof(ProtolMachine));
	
	if(*pProtolMach)
	{
		(*pProtolMach)->u8ProtolElementCount = 0;
    (*pProtolMach)->u16TotalLength = 0;
    (*pProtolMach)->u16TotalCount = 0;
    (*pProtolMach)->u16SubLength = 0;
    (*pProtolMach)->u16SubCount = 0;
    (*pProtolMach)->u8ProtolElementLength = 0;
    (*pProtolMach)->u16DataIndex = 0;
    (*pProtolMach)->u16CommandIndex = 0;

    memset((*pProtolMach)->u8aProtolPackageContent, 0, (sizeof(uint8_t) * MAX_PROTOLPACKAGE_LENGTH));
    memset((*pProtolMach)->sProtolElementSets, 0, (sizeof(ProtolElement) * MAX_PROTOLELEMENT_COUNT));
		
		return true;
	}
	else
	{
		return false;
	}

}

void ResetProtolMachine(ProtolMachine *pProtolMach)
{
    pProtolMach->u16TotalLength = 0;
    pProtolMach->u16TotalCount = 0;
    pProtolMach->u16SubLength = 0;
    pProtolMach->u16SubCount = 0;
    pProtolMach->u8ProtolElementCount = 0;
}

void SetDataOnlyFlag(ProtolMachine *pProtolMach, bool flag)
{
    pProtolMach->bLengthIsDataOnly = flag;
}

bool AddProtolElement(ProtolMachine *pProtolMach, ProtolElement *pProtolElem)
{
    memcpy(pProtolMach->sProtolElementSets + pProtolMach->u8ProtolElementLength, pProtolElem, sizeof(ProtolElement));
    if(pProtolElem->tProtolElementType == eProtolElementData) {
        if(pProtolMach->u16DataIndex != 0)
            return false;
        else
            pProtolMach->u16DataIndex = pProtolMach->u8ProtolElementLength;
    }

    if(pProtolElem->tProtolElementType == eProtolElementCommand) {
        if(pProtolMach->u16CommandIndex != 0)
            return false;
        else
            pProtolMach->u16CommandIndex = pProtolMach->u8ProtolElementLength;
    }

    pProtolMach->u8ProtolElementLength ++;
    return true;
}


//将解析出来的数据保存到输出数组中，可以多包数据粘包
//data: 接收到的数据
//length：接收到的数据的长度
//data_vld: 解析出来的合法数据,注意，这里面可能包含多包数据，每包数据的长度在length_content中
//length_content:  每一包合法数据的长度
//package_cnt: 一共有多少包合法数据
ProtolElementStatus ProtolMachineElementDecode(ProtolMachine *pProtolMach, uint8_t *data, uint16_t length,
    uint8_t *data_vld, uint16_t *length_content, uint8_t *package_cnt)
{
    ProtolElementStatus state = eProtolElementContinue;
    uint8_t pack_cnt = 0;
    uint16_t last_pack_length = 0;
    *package_cnt = 0;
    *length_content = 0;

    for(uint16_t i = 0; i < length; i ++) {
        if(pProtolMach->sProtolElementSets[pProtolMach->u8ProtolElementCount].tProtolElementType == eProtolElementConstant) {
            //如果是对比类的协议元素，如：这个协议元素是个协议头
            //那么就对比是否和协议头相同
            if(pProtolMach->sProtolElementSets[pProtolMach->u8ProtolElementCount].ProtolElementContent[pProtolMach->u16SubCount] != data[i]) {
                //如果对比失败，则子元素内部计数器清零，然后接着
                //对比后面数据看能否找到符合要求的

                ResetProtolMachine(pProtolMach);
            } else {
                pProtolMach->u8aProtolPackageContent[pProtolMach->u16TotalCount] = data[i];
                pProtolMach->u16SubCount ++;
                pProtolMach->u16TotalCount ++;

                //如果子计数器已经达到了协议元素的长度
                //则讲子计数器清零，将子元素标号自增
                if(pProtolMach->u16SubCount == pProtolMach->sProtolElementSets[pProtolMach->u8ProtolElementCount].
                                    u16ProtolElementLength) {
                    pProtolMach->u16SubCount = 0;
                    pProtolMach->u8ProtolElementCount ++;
                }
            }
        } else if(pProtolMach->sProtolElementSets[pProtolMach->u8ProtolElementCount].tProtolElementType
                        == eProtolElementLength){
                //如果有数据长度字段，那么接收到数据长度的时候会按照这里数据长度
                //计算，这个主要用于可变长度的数据帧的处理
                //如果没有数据长度字段，那么数据段的长度会按照注册数据段的时候指定
                //的长度来，这个主要用于定长度的协议帧
                pProtolMach->u8aProtolPackageContent[pProtolMach->u16TotalCount] = data[i];
                pProtolMach->sProtolElementSets[pProtolMach->u8ProtolElementCount].ProtolElementContent[pProtolMach->u16SubCount] = data[i];

                pProtolMach->u16SubCount ++;
                pProtolMach->u16TotalCount ++;

                //如果子计数器已经达到了协议元素的长度
                //则讲子计数器清零，将子元素标号自增
                //length字段接收完成，开始计算长度
                if(pProtolMach->u16SubCount == pProtolMach->sProtolElementSets[pProtolMach->u8ProtolElementCount].
                                    u16ProtolElementLength) {

                    uint16_t length_val = 0;

                    if(pProtolMach->sProtolElementSets[pProtolMach->u8ProtolElementCount].u16ProtolElementLength == 1)
                        length_val = pProtolMach->sProtolElementSets[pProtolMach->u8ProtolElementCount].ProtolElementContent[0];
                    else if(pProtolMach->sProtolElementSets[pProtolMach->u8ProtolElementCount].u16ProtolElementLength == 2)
                        length_val = ((pProtolMach->sProtolElementSets[pProtolMach->u8ProtolElementCount].ProtolElementContent[1] << 8) |
                                    pProtolMach->sProtolElementSets[pProtolMach->u8ProtolElementCount].ProtolElementContent[0]);


                    //用来确定这一帧数据的总长度
                    //主要目的就是确定好数据段的长度
                    if(pProtolMach->bLengthIsDataOnly) {
                        //如果是仅仅数据部分的长度
                        //那么直接把这个长度给到数据
                        //字段就行
                        pProtolMach->sProtolElementSets[pProtolMach->u16DataIndex].
                                u16ProtolElementLength = length_val;
                    } else {
                        //如果不仅仅是数据部分的长度
                        //那么表示这个是整个协议包的长度
                        //此时，需要用整个包的数据长度
                        //减掉除了数据包之外的字段的长度
                        //这样就求出数据端的长度了
                        uint16_t other_segLength = 0;
                        for(uint8_t ss = 0; ss < pProtolMach->u8ProtolElementLength; ss ++) {
                            if(pProtolMach->sProtolElementSets[ss].tProtolElementType != eProtolElementData)
                                other_segLength += pProtolMach->sProtolElementSets[ss].u16ProtolElementLength;
                        }

                        pProtolMach->sProtolElementSets[pProtolMach->u16DataIndex].
                                u16ProtolElementLength = length_val - other_segLength;

                    }

                    if(pProtolMach->sProtolElementSets[pProtolMach->u16DataIndex].
                            u16ProtolElementLength == 0) {
                        //如果数据长度为0，则表示这包数据仅仅是有命令字
                        //数据部分为0，直接跳过这个字段
                        pProtolMach->u16SubCount = 0;
                        pProtolMach->u8ProtolElementCount += 2;
                    } else if(pProtolMach->sProtolElementSets[pProtolMach->u16DataIndex].
                              u16ProtolElementLength < 0) {
                        //如果长度小于0，则收到错误包了
                        //重新初始化协议处理机，重新接收数据
                        ResetProtolMachine(pProtolMach);
                    }
                    else {
                        pProtolMach->u16SubCount = 0;
                        pProtolMach->u8ProtolElementCount ++;
                    }
                }


        } else if(pProtolMach->sProtolElementSets[pProtolMach->u8ProtolElementCount].tProtolElementType
                        == eProtolElementData){

                pProtolMach->u8aProtolPackageContent[pProtolMach->u16TotalCount] = data[i];
                pProtolMach->u16SubCount ++;
                pProtolMach->u16TotalCount ++;

                //如果子计数器已经达到了协议元素的长度
                //则讲子计数器清零，将子元素标号自增
                if(pProtolMach->u16SubCount == pProtolMach->sProtolElementSets[pProtolMach->u8ProtolElementCount].
                                    u16ProtolElementLength) {
                    pProtolMach->u16SubCount = 0;
                    pProtolMach->u8ProtolElementCount ++;
                }


        } else {
            pProtolMach->u8aProtolPackageContent[pProtolMach->u16TotalCount] = data[i];
            pProtolMach->u16SubCount ++;
            pProtolMach->u16TotalCount ++;

            //如果子计数器已经达到了协议元素的长度
            //则讲子计数器清零，将子元素标号自增
            if(pProtolMach->u16SubCount == pProtolMach->sProtolElementSets[pProtolMach->u8ProtolElementCount].
                                    u16ProtolElementLength) {
                pProtolMach->u16SubCount = 0;
                pProtolMach->u8ProtolElementCount ++;
            }
        }

        if(pProtolMach->u8ProtolElementLength
                == pProtolMach->u8ProtolElementCount) {

            //将数据保存到输出数组中
            memcpy(data_vld + last_pack_length, pProtolMach->u8aProtolPackageContent, pProtolMach->u16TotalCount);
            //记录这组有效数据的长度
            length_content[pack_cnt] = pProtolMach->u16TotalCount;
            //记录上一包有效数据的长度，便于下一包有效数据的保存
            last_pack_length = pProtolMach->u16TotalCount;

            pack_cnt ++;

            ResetProtolMachine(pProtolMach);

            state = eProtolElementDone;
        } else {

        }
    }

    //记录输出数据中一共有多少包有效的数据包
    *package_cnt = pack_cnt;
    return state;
}

bool ProtolMachineDecode(ProtolMachine *pProtolMach, uint8_t *data, uint16_t length,
        uint8_t *data_vld, uint16_t *length_content, uint8_t *package_cnt)
{
    ProtolMachineElementDecode(pProtolMach, data, length, data_vld, length_content, package_cnt);
    return true;
}

//注册命令号以及该命令的处理函数
int RegistCommandInfo(ProtolMachine *pProtolMach, CommandInfo cmd_info)
{
	int cnt = 0;
	while (pProtolMach->cmdInfos[cnt].tCommandCallback) {
		/*-----------add cnt >> MAX_CMD_COUNT --------*/	
		cnt ++;
		if (cnt == MAX_COMMAND_CNT) {
			return ERR_CMDSET_FULL;	
		}
	}

	memcpy (&pProtolMach->cmdInfos[cnt], 
			&cmd_info, sizeof (CommandInfo));
	
	return 0;
}

//注册一组命令
int regist_command_info_array(ProtolMachine *pack_info, CommandInfo *cmd_info, int cmd_cnt)
{
	int i;
	for (i = 0; i < cmd_cnt; i ++) {
		if (RegistCommandInfo(pack_info, cmd_info[i])	== ERR_CMDSET_FULL) {
			break;	
		}
	}

	return i;
}

//获取命令号以及相应的处理函数
int GetCommandIndex(int cmd_no, CommandInfo *cmd_infos)
{
	int cnt = 0;
	int cmd_index = -1;
	while (cmd_infos[cnt].u32CommandNum != cmd_no && 
			cmd_infos[cnt].tCommandCallback != NULL) {
		cnt ++;	
	}
	
	//-------add cnt == MAX_CMD_CNT process!--------

	if (cmd_infos[cnt].tCommandCallback != NULL) {
		cmd_index = cnt;
	}

	return cmd_index;
}
